package com.netty.io.asyncio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author zhangbq
 * @since 2020-12-04 16:28
 * serverSocketChannel ->> socketserver ->> selector ->> selectKey ->> 去channel中的Buffer处理数据
 **/
public class NioServer {
    //不用再使用多个线程循环去accept去处理不同客户端的连接请求

    public static void main(String[] args) {
        try {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            Selector selector = Selector.open();

            //绑定端口，在服务端监听请求
            serverSocketChannel.socket().bind(new InetSocketAddress(6666));
            //配置为非阻塞，异步
            serverSocketChannel.configureBlocking(false);
            //向ServerSocket通道注册需要监听的事件这里是监听请求事件， OP_ACCEPT
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            //循环等待客户端的连接
            while (true) {
                if (selector.select(1000) == 0){
                    System.out.println("服务器等待 1 秒，没有请求连接，返回");
                }

                //如果返回的不是大于0， 其他时间
                //如果返回的大于0，表示已经有关注的事件到来，这时候需要取出处理
                //通过selectKeys 获取通道
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                //使用迭代器处理
                Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
                while (keyIterator.hasNext()){
                    //根据Key，判断对应通道的事件，做出不同的响应处理
                    SelectionKey key = keyIterator.next();
                    //一个客户端处理请求
                    if(key.isAcceptable()){
                        //有客户端请求，需要注册一个读的事件，然后服务器去处理客户端请求，每一个读写事件与Buffer相关联
                        SocketChannel socketChannel = serverSocketChannel.accept();  //客户端会生成一个socketChannel需要被处理的通道
                        System.out.println("客户端连接成功，创建出SocketChannel ： " + socketChannel.hashCode());

                        //客户端注册一个Read的请求事件到socketChannel之后就可以释放资源去做别的业务了
                        socketChannel.configureBlocking(false); //这里释放资源
                        socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                    }

                    //处理客户端的读事件
                    if (key.isReadable()){
                        //取到通道,取到Buffer
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        //取到通道关联的Buffer，然后往里面读取数据回来东西
                        ByteBuffer buffer = (ByteBuffer) key.attachment();
                        socketChannel.read(buffer);
                        System.out.println("从客户端获取到数据 ： " + new String(buffer.array()));
                    }
                    //最后需要从集合中移除这个事件(SelectionKey)，避免重复操作
                    keyIterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
